﻿using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MSocketCore.Model;
using MSocketCore.MSocket;

namespace MSocketCore.Hub
{
    public class MessageClient
    {
        private readonly IOptions<MConfig> _config;
        private readonly ILogger<MessageClient> _logger;
        private readonly IServiceProvider _serviceProvider;
        private HubConnection mainconn;

        private HubConnection testconn;

        public MessageClient(IOptions<MConfig> config, ILogger<MessageClient> logger, IServiceProvider serviceProvider)
        {
            _config = config;
            _logger = logger;
            _serviceProvider = serviceProvider;
        }

        public async Task<bool> Connect()
        {
            
            //初始化hub连接
            testconn = new HubConnectionBuilder().WithUrl(_config.Value.SignalrUrl + "/serverHub", options =>
                {
                    options.AccessTokenProvider = () => Task.FromResult(_config.Value.Secret);
                    options.Headers.Add("X-Server", "1");
                }).WithAutomaticReconnect()
                .Build();

            mainconn = new HubConnectionBuilder().WithUrl(_config.Value.SignalrUrl + "/maintainHub", options =>
            {
                options.AccessTokenProvider = async () => { return _config.Value.Secret; };
                options.Headers.Add("X-Server", "1");
            }).WithAutomaticReconnect().Build();


            //重连失败关闭策略
            testconn.Closed += OnClosed;
            mainconn.Closed += OnClosed;

            //重连报告
            testconn.Reconnecting += OnReconnecting;
            mainconn.Reconnecting += OnReconnecting;

            testconn.Reconnected += OnReconnected;
            mainconn.Reconnected += OnReconnected;

            //事件订阅
            testconn.On<string, string>("ReceiveMessage", ReceiveMessage);
            mainconn.On<string, string>("notify", GetNotify);

            StartConnect();
            return true;
        }

        private Task OnReconnected(string arg)
        {
            _logger.LogWarning("hub reconnected");
            return Task.CompletedTask;
        }

        private void StartConnect()
        {
            var cancletoken = new CancellationToken();
            ConnectWithRetryAsync(testconn, "test", cancletoken);
            ConnectWithRetryAsync(mainconn, "maintain", cancletoken);
        }

        private async Task OnClosed(Exception arg)
        {
            _logger.LogWarning("hub is lost");
            await Task.Delay(5 * 1000);
            StartConnect();
        }

        private Task OnReconnecting(Exception arg)
        {
            _logger.LogWarning(" hub reconnecting ... ...");
            return Task.CompletedTask;
        }

        /// <summary>
        ///     首次连接失败判断
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public async Task<bool> ConnectWithRetryAsync(HubConnection connection, string name, CancellationToken token)
        {
            while (true)
                try
                {
                    if (connection.State == HubConnectionState.Connected)
                    {
                        return true;
                    }
                    await connection.StartAsync(token);
                    _logger.LogWarning(name + " connection successfully");
                    return true;
                }
                catch when (token.IsCancellationRequested)
                {
                    return false;
                }
                catch
                {
                    _logger.LogWarning(name + " connection failed");
                    await Task.Delay(5000);
                }
        }

        public void DisConnect()
        {
            testconn.StopAsync();
            mainconn.StopAsync();
        }

        private void ReceiveMessage(string name, string message)
        {
            _logger.LogInformation($"HubServer:[ReceiveMessage]{name } {message}");
            var host = _serviceProvider.GetService<ServerContainer>();
            host.Send();
        }

        public void SendMessage(string name, string msg)
        {
            testconn.InvokeAsync("SendMessage", name, msg);
        }

        public void SendInfo(string groupname, string msg)
        {
            mainconn.InvokeAsync("PushInfo", groupname, msg);
        }

        private byte[] StringToByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");//去掉空格
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        private void GetNotify(string sn, string messsage)
        {
            _logger.LogInformation($"HubServer:[Notify][{sn}]{messsage}");
            var dataBytes = StringToByte(messsage);
            var host = _serviceProvider.GetService<ServerContainer>();
            host.Send(sn, dataBytes);
        }

    }
}